package com.api.client.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.scribe.builder.api.Api;
import org.scribe.builder.api.DefaultApi10a;
import org.scribe.model.OAuthConfig;
import org.scribe.model.OAuthRequest;
import org.scribe.model.Response;
import org.scribe.model.SignatureType;
import org.scribe.model.Token;
import org.scribe.model.Verb;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;

import com.api.test.OAuthServiceConfig;

public class OAuthProvider {

	private Log logger = LogFactory.getLog(getClass());

	private final OAuthService service;

	private final OAuthServiceConfig oauthConfig;

	public OAuthProvider(OAuthServiceConfig oauthConfig) {

		if (oauthConfig == null) {

			throw new IllegalArgumentException("OAuthConfig cannot be null");
		}

		if (oauthConfig.getConsumerKey() == null || oauthConfig.getConsumerSecret() == null) {

			throw new IllegalArgumentException("Consumer tokens cannot be null");
		}

		Api api = getDefaultApi(oauthConfig.getRequestEndpoint(), oauthConfig.getAccessEndpoint());
		this.service = api.createService(new OAuthConfig(oauthConfig.getConsumerKey(), oauthConfig.getConsumerSecret(), "oob",
				SignatureType.Header, null, null));

		this.oauthConfig = oauthConfig;
	}

	public void sign(OAuthRequest request, String accessKey, String accessSecret) {

		Token accessToken = new Token(accessKey, accessSecret);

		service.signRequest(accessToken, request);

		logger.info(String.format("Request signed with AccessKeys: ['%s', '%s'] - CosumerKeys: ['%s' , '%s']", accessKey, accessSecret,
				oauthConfig.getConsumerKey(), oauthConfig.getConsumerSecret()));
	}

	public Token getAccessToken(String username, String password) {

		if (username == null || password == null) {

			throw new IllegalStateException("Username and password cannot be null");
		}

		Token requestToken = null;

		try {
			requestToken = service.getRequestToken();
		} catch (Exception e) {
			throw new IllegalStateException(String.format("Couldn't get request token in endpoint '%s' for consumerKeys: ['%s', '%s']",
					oauthConfig.getRequestEndpoint(), oauthConfig.getConsumerKey(), oauthConfig.getConsumerSecret()), e);
		}

		OAuthRequest authorizeRequest = new OAuthRequest(Verb.POST, oauthConfig.getAuthorizationEndpoint());

		authorizeRequest.addBodyParameter("username", username);
		authorizeRequest.addBodyParameter("password", password);
		authorizeRequest.addBodyParameter("request_token", requestToken.getToken());

		Response authorizeResponse = authorizeRequest.send();

		if (!authorizeResponse.isSuccessful()) {

			throw new IllegalStateException(
					String.format(
							"Couldn't authorize api user in access endpoint '%s'- Username: '%s', Password: '%s',  RequestToken: '%s', consumerKeys: ['%s', '%s'] - Code: '%s' Message: '%s'",
							oauthConfig.getAuthorizationEndpoint(), username, password, requestToken.getToken(),
							oauthConfig.getConsumerKey(), oauthConfig.getConsumerSecret(), authorizeResponse.getCode(),
							authorizeResponse.getBody()));
		}

		try {

			Token accessToken = service.getAccessToken(requestToken, new Verifier(""));

			logger.info(String.format("User logged in: '%s'/'%s'", username, password));

			return accessToken;
		} catch (Exception e) {

			throw new IllegalStateException(String.format(
					"Couldn't get accessToken - Username: '%s', Password: '%s', RequestToken: ['%s', '%s'], consumerKeys: ['%s', '%s']",
					username, password, requestToken.getToken(), requestToken.getSecret(), oauthConfig.getConsumerKey(),
					oauthConfig.getConsumerSecret()), e);
		}
	}

	public OAuthServiceConfig getOauthConfig() {
		return this.oauthConfig;
	}

	public OAuthService getService() {
		return this.service;
	}

	private Api getDefaultApi(final String requestEndpoint, final String accessEndpoint) {

		return new DefaultApi10a() {
			@Override
			public String getRequestTokenEndpoint() {
				return requestEndpoint;
			}

			@Override
			public String getAccessTokenEndpoint() {
				return accessEndpoint;
			}

			@Override
			public String getAuthorizationUrl(Token requestToken) {
				return null;
			}
		};
	}
}
